/*
 * @Author: your name
 * @Date: 2021-08-13 22:24:54
 * @LastEditTime: 2021-08-16 18:35:38
 * @LastEditors: Please set LastEditors
 * @Description: In User Settings Edit
 * @FilePath: /面向过程io复用/poll.cpp
 */

#include <sys/socket.h>//socket基础api
#include <sys/types.h>
#include <string.h>//bzero
#include <fcntl.h>//fcntl

#include <sys/poll.h>//select
#include <netinet/in.h>//socket地址类型
#include <unistd.h>

#include <vector>
#include <iostream>
using namespace std;

#define exit_if(r, ...) if(r) {printf(__VA_ARGS__); printf("error no: %d error msg %s\n", errno, strerror(errno)); exit(1);}
using PollList=vector<pollfd>;

PollList fds;
pollfd temp;

char buf[1024];//应用层接受缓冲区
int len=0;
char buf_send[1024];//应用层发送缓冲区
int len_send=0;

void SetReuse(int fd)
{
    int reuse=1;
    setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&reuse,(socklen_t)sizeof reuse);
}
void SetNonBlock(int fd) {
    int flags = fcntl(fd, F_GETFL, 0);
    exit_if(flags<0, "fcntl failed");
    int r = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
    exit_if(r<0, "fcntl failed");
}
int Init_Socket()
{
    short port=10000;
    struct sockaddr_in seraddr;
    bzero(&seraddr,sizeof seraddr);
    seraddr.sin_addr.s_addr=INADDR_ANY;
    seraddr.sin_family=AF_INET;
    seraddr.sin_port=htons(port);

    int lfd=socket(AF_INET,SOCK_STREAM,0);
    exit_if(lfd==-1,"socket() error");
    SetNonBlock(lfd);
    SetReuse(lfd);
    exit_if(::bind(lfd,(const sockaddr*)&seraddr,(socklen_t)sizeof seraddr)==-1,"bind() error");
    exit_if(listen(lfd,100)==-1,"listen() error");
    return lfd;
}
void HandleWrite(PollList::iterator& it)
{
    send(it->fd,buf_send,sizeof buf_send,0);
    it->events&=~POLLOUT;//不手动将POLLOUT事件取消，则revents会一直检测到，从而重复发送
}
int HandleConnection(PollList::iterator& it,int flag)
{
    int ret;
    if(flag)
    {
        struct sockaddr_in cliaddr;
        socklen_t addrlen=sizeof cliaddr;
        bzero(&cliaddr,sizeof cliaddr);
        pollfd temp;
        ret=accept(it->fd,(struct sockaddr*)&cliaddr,&addrlen);
        exit_if(ret==-1,"accept()错误");
        temp.events=POLLIN;
        temp.fd=ret;
        fds.push_back(temp);
    }
    else
    {
        close(it->fd);
        fds.erase(it);
        it--;
    }
    return ret;
}
void HandleRead(PollList::iterator& it)
{
    len=recv(it->fd,buf,sizeof buf,0);
    exit_if(len==-1,"read() error");
    if(len==0)//*********
    {
        HandleConnection(it,false);
        return ;
    }
    cout<<"read "<<len<<" bytes: "<<buf<<endl;//*****telnet的问题，/r/n
    memcpy(buf_send,buf,sizeof buf);//有了这一行，才会触发POLLOUT事件；
    it->events|=POLLOUT;
}
void loop(int lfd)
{
    struct timeval time,timetemp;
    time.tv_sec=5;
    time.tv_usec=0;
    timetemp=time;
    for(;;)
    {
        //每次循环之前的动作
        memset(buf,'\0',sizeof buf);
        //memset(buf_send,'\0',sizeof buf_send);
        timetemp=time;
        int size=fds.size(),nready;
        nready=poll(&*fds.begin(),size,timetemp.tv_sec*1000);
        if(nready==0)
        {
            cout<<timetemp.tv_sec<<"s内暂无事件发生"<<endl;
            continue;
        }
        else if(nready==-1)
        {
            exit_if(1,"select()错误");
        }
        for(PollList::iterator it=fds.begin();it!=fds.end();it++)
        {
            int fd=it->fd;
            if(nready==0)
                break;
            else if(fd==lfd&&(it->revents&POLLIN))
            {
                HandleConnection(it,true);
                nready--;
            }
            else if(it->revents&POLLIN)
            {
                HandleRead(it);
                nready--;
            }
            else if(it->revents&POLLOUT)
            {
                HandleWrite(it);
                nready--;
            }
        }
    }
    close(lfd);
}
int main()
{
    int lfd=Init_Socket();

    bzero(&temp,sizeof temp);
    temp.fd=lfd;
    temp.events=POLLIN;
    fds.push_back(temp);

    loop(lfd);

    return 0;
}
